﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Media;
using Gronia.WPF.Helpers;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.ComponentModel;
using System.Windows.Data;

namespace Gronia.WPF.Controls
{
    public partial class BusyIndicator : ContentControl
    {
        #region IsBusy
        public static readonly DependencyProperty IsBusyProperty = DependencyProperty.Register("IsBusy", typeof(bool), typeof(BusyIndicator), new PropertyMetadata(false, IsBusyPropertyValueChanged));
        public bool IsBusy { get { return (bool)GetValue(IsBusyProperty); } set { SetValue(IsBusyProperty, value); } }
        private static void IsBusyPropertyValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            BusyIndicator busy = (BusyIndicator)sender;

            if (!busy.IsBusy && busy.FocusAfter != null)
            {
                busy.FocusAfter.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(delegate { busy.FocusAfter.Focus(); busy.FocusAfter = null; }));
            }
        }
        #endregion

        #region Focus After
        public static readonly DependencyProperty FocusAfterProperty = DependencyProperty.Register("FocusAfter", typeof(FrameworkElement), typeof(BusyIndicator));
        public FrameworkElement FocusAfter { get { return (FrameworkElement)GetValue(FocusAfterProperty); } set { SetValue(FocusAfterProperty, value); } }
        #endregion

        #region BusyTitle
        public static readonly DependencyProperty BusyTitleProperty = DependencyProperty.Register("BusyTitle", typeof(object), typeof(BusyIndicator));
        public object BusyTitle { get { return GetValue(BusyTitleProperty); } set { SetValue(BusyTitleProperty, value); } }
        #endregion

        #region BusyContent
        public static readonly DependencyProperty BusyContentProperty = DependencyProperty.Register("BusyContent", typeof(object), typeof(BusyIndicator));
        public object BusyContent { get { return GetValue(BusyContentProperty); } set { SetValue(BusyContentProperty, value); } }
        #endregion

        #region BusyContentTemplate
        public static readonly DependencyProperty BusyContentTemplateProperty = DependencyProperty.Register("BusyContentTemplate", typeof(DataTemplate), typeof(BusyIndicator));
        public DataTemplate BusyContentTemplate { get { return (DataTemplate)GetValue(BusyContentTemplateProperty); } set { SetValue(BusyContentTemplateProperty, value); } }
        #endregion

        #region OverlayStyle
        public static readonly DependencyProperty OverlayStyleProperty = DependencyProperty.Register("OverlayStyle", typeof(Style), typeof(BusyIndicator));
        public Style OverlayStyle { get { return (Style)GetValue(OverlayStyleProperty); } set { SetValue(OverlayStyleProperty, value); } }
        #endregion

        #region BusyImage
        public static readonly DependencyProperty BusyImageProperty = DependencyProperty.Register("BusyImage", typeof(ImageSource), typeof(BusyIndicator), new PropertyMetadata(BusyIndicator.OnBusyImagePropertyChanged));
        public ImageSource BusyImage { get { return (ImageSource)GetValue(BusyImageProperty); } set { SetValue(BusyImageProperty, value); } }

        private static Rotation3DAnimation CreateAnimation(Vector3D Axis, double From, double To, double BeginTime)
        {
            Rotation3DAnimation animation = new Rotation3DAnimation();
            animation.From = new AxisAngleRotation3D(Axis, From);
            animation.To = new AxisAngleRotation3D(Axis, To);
            animation.Duration = new Duration(TimeSpan.FromSeconds(0.5));
            animation.BeginTime = TimeSpan.FromSeconds(BeginTime);
            return animation;
        }

        private static void SetTarget(Rotation3DAnimation animation, BusyIndicator busy)
        {
            Storyboard.SetTarget(animation, busy.PART_BusyImageVisual);
            Storyboard.SetTargetProperty(animation, new PropertyPath("(Viewport2DVisual3D.Transform).(RotateTransform3D.Rotation)"));
        }

        private static void OnBusyImagePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            BusyIndicator busy = (BusyIndicator)sender;

            if (busy.BusyImage == null)
            {
                busy.story.Stop();
                busy.story.Children.Clear();
                ImageHelper.SetAnimatedSource(busy.PART_BusyImage, null);
                return;
            }

            if (busy.BusyImageType == BusyImageType.GIF)
            {
                if (ImageHelper.GetAnimatedSource(busy.PART_BusyImage) == null || ImageHelper.GetAnimatedSource(busy.PART_BusyImage) != busy.BusyImage)
                {
                    ImageHelper.SetAnimatedSource(busy.PART_BusyImage, busy.BusyImage);
                }
            }
            else
            {
                ImageHelper.SetAnimatedSource(busy.PART_BusyImage, null);

                Binding bind = new Binding("BusyImage");
                bind.Source = busy;
                busy.PART_BusyImage.SetBinding(Image.SourceProperty, bind);

                if (busy.BusyImageType == BusyImageType.Normal)
                {
                    busy.story.Stop();
                    busy.story.Children.Clear();
                }
                else if (busy.BusyImageType == BusyImageType.RotateHorizontal3D)
                {
                    Rotation3DAnimation animation1 = CreateAnimation(new Vector3D(1, 0, 0), 0, 90, 0.5);
                    Rotation3DAnimation animation2 = CreateAnimation(new Vector3D(1, 0, 0), -90, 0, 1);
                    SetTarget(animation1, busy);
                    SetTarget(animation2, busy);

                    busy.story.Children.Clear();

                    busy.story.Children.Add(animation1);
                    busy.story.Children.Add(animation2);

                    busy.story.RepeatBehavior = RepeatBehavior.Forever;
                    busy.story.Begin();
                }
                else if (busy.BusyImageType == BusyImageType.RotateVertical3D)
                {
                    Rotation3DAnimation animation1 = CreateAnimation(new Vector3D(0, 1, 0), 0, 90, 0.5);
                    Rotation3DAnimation animation2 = CreateAnimation(new Vector3D(0, 1, 0), -90, 0, 1);
                    SetTarget(animation1, busy);
                    SetTarget(animation2, busy);

                    busy.story.Children.Clear();

                    busy.story.Children.Add(animation1);
                    busy.story.Children.Add(animation2);

                    busy.story.RepeatBehavior = RepeatBehavior.Forever;
                    busy.story.Begin();
                }
                else if (busy.BusyImageType == BusyImageType.Rotate)
                {
                    Rotation3DAnimation animation1 = CreateAnimation(new Vector3D(0, 0, 1), 360, 240, 0.5);
                    Rotation3DAnimation animation2 = CreateAnimation(new Vector3D(0, 0, 1), 240, 120, 1);
                    Rotation3DAnimation animation3 = CreateAnimation(new Vector3D(0, 0, 1), 120, 0, 1.5);

                    SetTarget(animation1, busy);
                    SetTarget(animation2, busy);
                    SetTarget(animation3, busy);

                    busy.story.Children.Clear();

                    busy.story.Children.Add(animation1);
                    busy.story.Children.Add(animation2);
                    busy.story.Children.Add(animation3);

                    busy.story.RepeatBehavior = RepeatBehavior.Forever;
                    busy.story.Begin();
                }
            }
        }
        #endregion

        #region BusyImageType
        public static readonly DependencyProperty BusyImageTypeProperty = DependencyProperty.Register("BusyImageType", typeof(BusyImageType), typeof(BusyIndicator), new PropertyMetadata(BusyImageType.None, new PropertyChangedCallback(BusyIndicator.OnBusyImagePropertyChanged)));
        public BusyImageType BusyImageType { get { return (BusyImageType)GetValue(BusyImageTypeProperty); } set { SetValue(BusyImageTypeProperty, value); } }
        #endregion

        #region BusyLoadingImage
        public static readonly DependencyProperty BusyLoadingImageProperty = DependencyProperty.Register("BusyLoadingImage", typeof(ImageSource), typeof(BusyIndicator), new PropertyMetadata(new PropertyChangedCallback(OnBusyLoadingImagePropertyChanged)));
        public ImageSource BusyLoadingImage { get { return (ImageSource)GetValue(BusyLoadingImageProperty); } set { SetValue(BusyLoadingImageProperty, value); } }
        private static void OnBusyLoadingImagePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            BusyIndicator busy = (BusyIndicator)sender;

            if (busy.BusyLoadingImage == null)
            {
                ImageHelper.SetAnimatedSource(busy.PART_BusyLoadingImage, null);
                return;
            }

            if (busy.BusyLoadingImageType == BusyLoadingImageType.GIF)
            {
                if (ImageHelper.GetAnimatedSource(busy.PART_BusyLoadingImage) == null || ImageHelper.GetAnimatedSource(busy.PART_BusyLoadingImage) != busy.BusyLoadingImage)
                {
                    ImageHelper.SetAnimatedSource(busy.PART_BusyLoadingImage, busy.BusyLoadingImage);
                }
            }
            else
            {
                ImageHelper.SetAnimatedSource(busy.PART_BusyLoadingImage, null);
                Binding bind = new Binding("BusyLoadingImage");
                bind.Source = busy;
                busy.PART_BusyLoadingImage.SetBinding(Image.SourceProperty, bind);
            }
        }
        #endregion

        #region BusyLoadingImageType
        public static readonly DependencyProperty BusyLoadingImageTypeProperty = DependencyProperty.Register("BusyLoadingImageType", typeof(BusyLoadingImageType), typeof(BusyIndicator), new PropertyMetadata(BusyLoadingImageType.None, new PropertyChangedCallback(OnBusyLoadingImagePropertyChanged)));
        public BusyLoadingImageType BusyLoadingImageType { get { return (BusyLoadingImageType)GetValue(BusyLoadingImageTypeProperty); } set { SetValue(BusyLoadingImageTypeProperty, value); } }
        #endregion
    }
}